package com.kizuki.reflect;

import lombok.Data;
import org.junit.Test;
import org.springframework.lang.Nullable;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class TestReflection {

    @Test
    public void testGetClass() throws ClassNotFoundException {
        Class c1 = com.kizuki.reflect.Student.class;
        Class c2 = Class.forName("com.kizuki.reflect.Student");
        Class c3 = new com.kizuki.reflect.Student().getClass();

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c3 == c1 && c1 == c2);
    }

    @Test
    public void testCommonType() {
        Class cc1 = Object.class;
        Class cc2 = Comparable.class;
        Class cc3 = String[].class;
        Class cc4 = String[][].class;
        Class cc5 = int[][].class;
        Class cc6 = Override.class;
        Class cc7 = ElementType.class;
        Class cc8 = Integer.class;
        Class cc9 = Integer.TYPE;
        Class cc10 = void.class;
        Class cc11 = Class.class;

        System.out.println(cc1);
        System.out.println(cc2);
        System.out.println(cc3);
        System.out.println(cc4);
        System.out.println(cc5);
        System.out.println(cc6);
        System.out.println(cc7);
        System.out.println(cc8);
        System.out.println(cc9);
        System.out.println(cc10);
        System.out.println(cc11);
    }

    @Test
    public void testCreateClassTime() {
        /**
         * 访问static final 变量不会clinit
         * static final 修饰的会存入调用类的常量池
         */
        System.out.println(Person.M);

//        Person.test(); // 调用 static final 方法会 clinit

        System.out.println("尝试访问Person.m");
        System.out.println(Person.m);
    }

    @Test
    public void testClassUse() throws Exception {
        /**
         * Declared获得本类的private public 属性方法
         * 没有的是获得父亲和自己的public
         */
        Class cl = Class.forName("com.kizuki.reflect.Student");
        System.out.println(cl.getSuperclass());
        System.out.println(cl.newInstance());

        /**
         * 构造器
         */
        Constructor constructor = cl.getDeclaredConstructor(String.class);
        System.out.println(constructor.newInstance("学生A"));

        /**
         * 方法
         */
        Student student = new Student();
        Method say = cl.getDeclaredMethod("say", String.class);
        say.setAccessible(true); // 去掉检测
        say.invoke(student, "测试私有方法的调用");
        System.out.println("say 参数列表：" + say.getParameterTypes() + " say 返回：" + say.getReturnType());

        // 调用父类的public static
        Method test = cl.getMethod("test");
        test.invoke(student);

        /**
         * 属性
         */
        Field id = cl.getDeclaredField("id");
        id.set(student, 1024);
        System.out.println(student);

        /**
         * 注解
         */
        Annotation[] annotations = cl.getAnnotations();
        System.out.println(Arrays.toString(annotations)); //Inherited可以覆盖父类的注解

        boolean annotationPresent = cl.isAnnotationPresent(MyAnnotation.class);
        System.out.println("annotationPresent:" + annotationPresent);

        MyAnnotation annotation = (MyAnnotation)cl.getAnnotation(MyAnnotation.class);
        System.out.println(annotation);

        Field name = cl.getDeclaredField("name");
        System.out.println(name.getAnnotation(Nullable.class));

        //student类的say方法的参数注解
        Annotation[][] parameterAnnotations = say.getParameterAnnotations();
        Annotation[] parameterAnnotation = parameterAnnotations[0];
        for (Annotation a: parameterAnnotation) {
            if (a instanceof Nullable){
                System.out.println("存在方法参数注解a为Nullable");
            }
        }
    }

    @Test
    /**
     * 父类_clinit
     * 测试static final
     */
    public void testFinalStaticMethod(){
        Person.test();
    }

}

@Data
@SuppressWarnings("all")
@MyAnnotation(name = "student", likes = {"java", "c++"})
class Student extends Person {
    static {
        System.out.println("学生_clinit");
    }

    @Nullable
    String name;

    public int id = 0;

    public Student(String name) {
        this.name = name;
    }

    public Student() { }

    private void say(@Nullable String talk){
        System.out.println("学生说：" + talk);
    }
}

@Data
@MyAnnotation(name = "person", likes = {"java", "c++"})
class Person {
    static {
        System.out.println("父类_clinit");
    }

    public static final void test(){
        System.out.println("测试static final");
    }

    static int m = 1;
    static final int M = 122;
}
